using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using CatLib;
using CatLib.Container;
using Cysharp.Threading.Tasks;
using GameEntry;
using GameFramework.Core;
using GameFramework.Extensions;
using GameFramework.Procedure;
using UnityEngine;
using Application = CatLib.Application;

namespace GameFramework.App
{
    public abstract class AGameApp : MonoBehaviour, IGameApp, IBootstrap
    {
        private Application _application;
        private IGameLoader _gameLoader;
        
        [Inject]
        public IProcedureService ProcedureService { get; set; }

        public void Init(IGameLoader gameLoader)
        {
            _gameLoader = gameLoader;
            var bootstraps = GetAppBootstraps(_gameLoader.CurrentLogicAssembles);
            bootstraps.Insert(0, this);

            CatLib.App.That = _application = new Application();
            CatLib.App.OnNewApplication += OnNewApplication;
            
            _application.GetDispatcher().AddListener(ApplicationEvents.OnStartCompleted, OnStartCompleted);
            _application.OnAfterResolving<IInitialize>(inst => inst.Initialize());
            _application.Bootstrap(bootstraps.ToArray());
            _application.Init();
        }

        private void OnNewApplication(IApplication application)
        {
            if (application == _application)
                return;
            
            DestroyImmediate(gameObject);
        }

        private void Awake()
        {
            DontDestroyOnLoad(gameObject);
        }

        private void OnDestroy()
        {
            CatLib.App.OnNewApplication -= OnNewApplication;
            _application.Terminate();
            _application = null;
        }

        public void Reboot()
        {
            var gameLoader = _gameLoader;

            DestroyImmediate(gameObject);

            gameLoader.LoadGameLogicAssemblies().Forget();
        }

        public void Quit(int errorCode = 0)
        {
            DestroyImmediate(gameObject);
            UnityEngine.Application.Quit(errorCode);
        }

        public void Bootstrap()
        {
            CatLib.App.Instance<IGameApp>(this);
            CatLib.App.Instance<IGameLoader>(_gameLoader);
        }

        private static List<IBootstrap> GetAppBootstraps(IEnumerable<Assembly> assemblies)
        {
            var bootstrapType = typeof(IBootstrap);
            var bootstraps = (from assembly in assemblies from type in assembly.GetTypes() where type.IsClass && !type.IsAbstract where bootstrapType.IsAssignableFrom(type) let attribute = type.GetCustomAttribute<AppBootstrapAttribute>() where attribute != null let priority = attribute.Priority let bootstrap = (IBootstrap)Activator.CreateInstance(type) select (bootstrap, priority)).ToList();

            int Comparison((IBootstrap bootstrap, int priority) x, (IBootstrap bootstrap, int priority) y)
            {
                return y.priority.CompareTo(x.priority);
            }

            bootstraps.Sort(Comparison);
            return bootstraps.Select(item => item.bootstrap).ToList();
        }

        private void OnStartCompleted(object sender, EventArgs e)
        {
            CatLib.App.That.Inject(this);
            OnStartCompleted();
            ProcedureService.Run(_gameLoader.CurrentLogicAssembles);
        }

        protected abstract void OnStartCompleted();
    }
}